﻿using Devil;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

namespace GameToolkit
{
    public class CommandBufferBlit
    {
        Mesh mFullscreenTriangle;
        Material mMaterial;
        int mClearPass;
        int mCopyPass;
        int mBlitPass;
        int mDepthPass;
        int mStencilPass;
        int mStackPass;
        int sh_Color;
        int sh_MainTex;
        Matrix4x4 matrix;

        private CommandBufferBlit()
        {
            mFullscreenTriangle = new Mesh { name = "Fullscreen Triangle" };

            // Because we have to support older platforms (GLES2/3, DX9 etc) we can't do all of
            // this directly in the vertex shader using vertex ids :(
            mFullscreenTriangle.SetVertices(new List<Vector3>
                {
                    new Vector3(-1f, -1f, 0f),
                    new Vector3(-1f,  3f, 0f),
                    new Vector3( 3f, -1f, 0f)
                });
            mFullscreenTriangle.SetIndices(new[] { 0, 1, 2 }, MeshTopology.Triangles, 0, false);
            mFullscreenTriangle.UploadMeshData(false);

            var shader = Shader.Find("Hidden/BlitImg");
            if (shader != null)
            {
                mMaterial = new Material(shader);
#if UNITY_EDITOR
                mMaterial.hideFlags |= HideFlags.DontSave;
#endif
                mClearPass = mMaterial.FindPass("Clear");
                mBlitPass = mMaterial.FindPass("BlitImg");
                mCopyPass = mMaterial.FindPass("CpyImg");
                mDepthPass = mMaterial.FindPass("CpyDepth");
                mStencilPass = mMaterial.FindPass("StencilOnly");
                mStackPass = mMaterial.FindPass("StackImg");
            }
            sh_Color = Shader.PropertyToID("_Color");
            sh_MainTex = Shader.PropertyToID("_MainTex");
            matrix = Matrix4x4.identity;
        }

        ~CommandBufferBlit()
        {
            ParallelUnity.DestroyAutomatic(mFullscreenTriangle);
            ParallelUnity.DestroyAutomatic(mMaterial);
            mFullscreenTriangle = null;
            mMaterial = null;
        }

        public void Blit(CommandBuffer cbuffer, RenderTargetIdentifier src, RenderTargetIdentifier dest, Color color, int pass)
        {
            cbuffer.SetGlobalTexture(sh_MainTex, src);
            cbuffer.SetGlobalColor(sh_Color, color);
            cbuffer.SetRenderTarget(dest/*, BuiltinRenderTextureType.CameraTarget*/);
            cbuffer.DrawMesh(mFullscreenTriangle, matrix, mMaterial, 0, pass);
        }

        static CommandBufferBlit _blit;

#if UNITY_EDITOR
        [InitializeOnLoadMethodParallel(ExecutionOrder = InitializeOrder.First, IsOnMainThread = true), UnityEditor.MenuItem("Game Toolkit/Reset Blit Asset")]
        static void ResetAssets()
        {
            if(_blit != null)
            {
                ParallelUnity.DestroyAutomatic(_blit.mFullscreenTriangle);
                ParallelUnity.DestroyAutomatic(_blit.mMaterial);
            }
            _blit = null;

            //var shader = Shader.Find("Hidden/BlitImg");
            //if(shader != null)
            //{
            //    var settings 
            //}
        }
#endif

        public static Mesh fullscreenTriangle
        {
            get
            {
                if (_blit == null || _blit.mFullscreenTriangle == null)
                    _blit = new CommandBufferBlit();
                return _blit.mFullscreenTriangle;
            }
        }

        public static Material material
        {
            get
            {
                if (_blit == null || _blit.mMaterial == null)
                    _blit = new CommandBufferBlit();
                return _blit.mMaterial;
            }
        }

        public static int BlitPass
        {
            get
            {
                if (_blit == null || _blit.mMaterial == null)
                    _blit = new CommandBufferBlit();
                return _blit.mBlitPass;
            }
        }

        public static int StencilPass
        {
            get
            {
                if (_blit == null || _blit.mMaterial == null)
                    _blit = new CommandBufferBlit();
                return _blit.mStencilPass;
            }
        }

        public static int BlitClearPass
        {
            get
            {
                if (_blit == null || _blit.mMaterial == null)
                    _blit = new CommandBufferBlit();
                return _blit.mClearPass;
            }
        }

        public static int BlitCopyPass
        {
            get
            {
                if (_blit == null || _blit.mMaterial == null)
                    _blit = new CommandBufferBlit();
                return _blit.mCopyPass;
            }
        }

        public static int BlitDepthPass
        {
            get
            {
                if (_blit == null || _blit.mMaterial == null)
                    _blit = new CommandBufferBlit();
                return _blit.mDepthPass;
            }
        }

        public static int BlitStackPass
        {
            get
            {
                if (_blit == null || _blit.mMaterial == null)
                    _blit = new CommandBufferBlit();
                return _blit.mStackPass;
            }
        }

        public static CommandBufferBlit EnsureAssets()
        {
            if (_blit == null || _blit.mFullscreenTriangle == null || _blit.mMaterial == null)
            {
                if (_blit != null)
                {
                    ParallelUnity.DestroyAutomatic(_blit.mFullscreenTriangle);
                    ParallelUnity.DestroyAutomatic(_blit.mMaterial);
                }
                _blit = new CommandBufferBlit();
            }
            var mesh = _blit.mFullscreenTriangle;
            var mat = _blit.mMaterial;
            if (mat == null || mesh == null)
                throw new System.Exception(string.Format("Invaild blit asset of \"{0}\".",
                    mesh == null ? "mesh" : (mat == null ? "material" : "mesh & material")));
            return _blit;
        }

        public static void SetSRPKeyword()
        {
            var mat = material;
            if (mat == null)
                return;
            string keyword = null;
            var srp = GraphicsSettings.currentRenderPipeline;
#if SRP_URP
            if (srp is UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset)
                keyword = "_SRP_URP";
#endif
            //if (!string.IsNullOrEmpty(keyword))
            //{
            //    material.EnableKeyword(keyword);
            //}
            //else
            //{
            var keys = mat.enabledKeywords;
            for (int i = 0; i < keys.Length; i++)
            {
                if (keys[i].name.StartsWith("_SRP_"))
                {
                    if (keys[i].name != keyword)
                        mat.DisableKeyword(keys[i]);
                }
            }
            if (!string.IsNullOrEmpty(keyword))
            {
                mat.EnableKeyword(keyword);
            }
            //}
        }

        public static void Copy(CommandBuffer cbuffer, RenderTargetIdentifier src, RenderTargetIdentifier dest)
        {
            var blit = EnsureAssets();
            blit.Blit(cbuffer, src, dest, Color.white, blit.mCopyPass);
        }

        public static void Blit(CommandBuffer cbuffer, RenderTargetIdentifier src, RenderTargetIdentifier dest)
        {
            var blit = EnsureAssets();
            blit.Blit(cbuffer, src, dest, Color.white, blit.mBlitPass);
        }

    }
}